CloudFormationでサイズの大きいSwaggerファイルを参照させる方法
はじめに
こんにちは。大阪オフィスの林です。
CloudFormationを使ってAPI Gatewayをプロビジョニングする際、AWS::Serverless::Api
リソースを利用することで、S3に格納されているSwaggerファイルを参照させながらAPI Gatewayをプロビジョニングすることが出来ます。HowTo的な部分は下記のブログをご参照頂ければと思います。
今回CloudFormationでAWS::Include
というモジュールを使ってS3に格納されているSwaggerファイルを参照させながらAPI Gatewayをプロビジョニングしていたのですが、参照しているSwaggerファイルが一定のサイズを超えるとTransform AWS::Include failed with: The specified S3 object is too big.
となりCloudFormationでのプロビジョニングに失敗してしまいました。
今回はこのエラーに対する1つの対処を検討したのでその内容をまとめておきたいと思います。(他にも対処の方法はあるかと思うのですがワークアラウンドの1つとしてご紹介させて頂きます)
制約の確認
上述した『一定のサイズを超えると』という部分がこの制約に引っかかっていたのではないかと考えています。実際にS3に格納したSwaggerファイルを確認すると500KBを超えていました。他の比較的サイズの小さい(100KB~200KB程度)Swaggerファイルではプロビジョニングが成功していたのでこの制約に引っかかっていたと考えて概ね問題ないでしょう。
Amazon S3 テンプレート URL を使用して S3 オブジェクトとして渡す場合は 460,800 バイトです。ただし、CloudFormation はテンプレート内のマクロを連続的に処理するため、処理中にテンプレートの一時的な状態が更新されます。このため、処理中のテンプレートのサイズは、完全に処理されたテンプレートの許容サイズを一時的に超える場合があります。CloudFormation では、このような処理中のテンプレートにバッファーで対処できます。ただし、テンプレートやマクロを設計する際は、処理済みのスタックテンプレートの最大許容サイズに留意してください。
AWS::Includeを使ってた時のコード
AWS::Include
を使用してS3に格納されているSwaggerファイルのパスを指定しています。このコードではAWS::Include
を使用しているためSwaggerのファイルサイズ次第では上述の制約に引っかかりCloudFormationでのプロビジョニングに失敗してしまいます。
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: "APIGateway created by Classmethod" Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Parameters: - swaggerpath Parameters: swaggerpath: Description: Please type the swaggerpath. Type: String Default: s3://swagger-test-backet/swagger.json Resources: ApiGateway: Type: AWS::Serverless::Api Properties: EndpointConfiguration: REGIONAL StageName: prod OpenApiVersion: '3.0.0' DefinitionBody: 'Fn::Transform': Name: 'AWS::Include' Parameters: Location: !Sub ${swaggerpath} (省略)
AWS::Include
を使うメリットの1つとして、CloudFormationで作成したリソースに変更が無くてもS3に格納されたSwaggerファイルが更新されていれば、そのSwaggerファイルの更新を把握して変更セットを実行することが可能という点があげられます。しかし今回はサイズの制約に引っかかってしまったためAWS::Include
が使用できません。
どうしたか?
今回は下記のコードに変更をしました。S3に格納されたSwaggerファイルを参照するという部分は変わらないのですが、AWS::Include
を使わずにDefinitionUri
でS3に格納されたSwaggerファイルを参照するように変更しています。ただしAWS::Include
を使用していないので、この場合S3に格納されたSwaggerファイルが更新されても、その変更をCloudFormationは認識できません。故に変更セットが実行できません。そこでDefinitionUri
の中にVersion
というパラメータを持たせて、S3で管理されているオブジェクトのバージョンIDを指定することで、Swaggerファイルの変更をCloudFormationに認識させ変更セットを実行させることとしました。※バージョンIDを作成するためにS3バケットのバージョニングを有効にする必要があります。
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: "APIGateway created by Classmethod" Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Parameters: - Corporationswaggerpath Parameters: swaggerbacket: Description: Please type the swaggerbacket. Type: String Default: swagger-test-backet swaggerfilename: Description: Please type the swaggerfilename. Type: String Default: swagger.json swaggerfileversion: Description: Please type the swaggerfileversion. Type: String Default: "" Resources: ApiGateway: Type: AWS::Serverless::Api Properties: EndpointConfiguration: REGIONAL StageName: prod OpenApiVersion: '3.0.0' #この部分(↓)を変えた!!! DefinitionUri: Bucket: !Sub ${swaggerbacket} Key: !Sub ${swaggerfilename} Version: !Sub ${swaggerfileversion} (省略)
やってみた
まずはTransform AWS::Include failed with: The specified S3 object is too big.
というエラーで失敗していたCloudFormationが成功するか見ていきます。スタック作成直後にエラーになっていましたがその部分は問題なくクリアしているように見えます。
CloudFormationでのプロビジョニングが正常終了しました。
次にSwaggerのファイルを更新してAPI Gatewayのリソースに更新がかかるか見ていきます。事前にS3から、更新したSwaggerファイルのバージョンIDを取得しておきます。上記で作成したCloudFormationのスタックから変更セットを実行していきます。今回はS3に格納されたSwaggerファイルのバージョンIDをCloudFormationのパラメータで渡すようなコードにしているので、更新したSwaggerファイルのバージョンIDをパラメータに指定します。
<変更前>
バージョンIDを変更したので変更セットが問題なく実行できることが分かります。では実行します。
最後にAPI Gatewayのリソースに更新がかかっているか確認しておきます。今回はとあるリソースのドキュメントのDescriptionだけ更新しています。
<更新前>
まとめ
それなりのシステム規模になるとSwagger自体のファイルサイズも大きくなっていくということもあると思いますので同じような事象に遭遇された際は是非参考にして頂けますと幸いです。もうちょっとスマートなやり方がないかと検討をしましたが正直見つけられなかったという背景もあり、多少アナログな対処ではありますがまとめさせて頂きました。
以上、大阪オフィスの林がお送りしました!